package com.rm.zeyou.pay.wxpay.util;

import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rm.zeyou.exception.ErrorCodeException;
import com.rm.zeyou.exception.ErrorCodeExceptionEnum;
import com.rm.zeyou.mapper.ApplicationConfigMapper;
import com.rm.zeyou.mapper.WxpayConfigMapper;
import com.rm.zeyou.pay.wxpay.common.WeChatRequestConstants;
import com.rm.zeyou.pay.wxpay.common.WechatH5GetMsgUrlConstants;
import com.rm.zeyou.pay.wxpay.entity.WxPayEntity;
import com.rm.zeyou.pay.wxpay.vo.*;
import com.rm.zeyou.pay.wxpay.wxenum.WxPayTypeEnum;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Log4j2
@Component
public class WxPayUtil {
    @Resource
    private WxPayEntity wxPayEntity;
    @Resource
    private WxpayConfigMapper wxpayConfigMapper;
    @Resource
    private ApplicationConfigMapper applicationConfigMapper;
    /**
     * 保存微信平台证书
     */
    //private final ConcurrentHashMap<String, AutoUpdateCertificatesVerifier> verifierMap = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Verifier> verifierMap = new ConcurrentHashMap<>();


    /**
     * 公众号支付校验
     * 通过access_token获得jsapi_ticket
     *
     * @param accessToken
     * @return
     */
    public String getJsapiTicket(String accessToken) {
        String requestUrl = WechatH5GetMsgUrlConstants.GetJsapiTicketByAccessToken.replace("ACCESS_TOKEN", accessToken);
        JSONObject jsonObject = HttpsUtils.request(requestUrl, "GET", null);
        log.info("jsonObject:{}", jsonObject);
        Integer errCode = jsonObject.getInteger("errcode");
        if (errCode == 0) {
            return (String) jsonObject.get("ticket");
        } else {
            log.warn("获取jsapi_ticke失败:" + jsonObject.toJSONString());
            return null;
        }
    }

    /**
     * 公众号支付校验
     * 获取JS-SDK签名
     *
     * @param url 当前网页的URL
     * @return
     */
    public Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String string1;
        String signature = "";

        //注意这里参数名必须全部小写，且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket +
                "&noncestr=" + nonce_str +
                "&timestamp=" + timestamp +
                "&url=" + url;
        log.info("string1:{}", string1);

        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8")); //对string1 字符串进行SHA-1加密处理
            signature = byteToHex(crypt.digest());  //对加密后字符串转成16进制
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        log.info("url:{}", url);
        log.info("signature:{}", signature);
        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        ret.put("appid", applicationConfigMapper.selectOneData("account_appid"));

        return ret;
    }

    /**
     * 公众号支付校验
     * 进制转换
     *
     * @param hash
     * @return
     */
    private String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 公众号支付校验 生成随机字符串
     *
     * @return
     */
    private String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    /**
     * 公众号支付校验 生成时间戳字符串
     */
    private String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    /**
     * 功能描述:获取平台证书，自动更新
     * 注意：这个方法内置了平台证书的获取和返回值解密
     *
     * @return com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier
     * @author zhouwenjie
     * @date 2021/6/13 20:29
     */
    //public AutoUpdateCertificatesVerifier getVerifier(String serialNumber) {
    //    AutoUpdateCertificatesVerifier verifier = null;
    //    if (verifierMap.isEmpty() || !verifierMap.containsKey(serialNumber)) {
    //        verifierMap.clear();
    //        try {
    //            //刷新
    //            String mchId = wxpayConfigMapper.selectOneData("mchId");
    //            String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
    //            String mchSerialNo = wxpayConfigMapper.selectOneData("mchSerialNo");
    //            String apiV3Key = wxpayConfigMapper.selectOneData("apiV3Key");
    //            PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, getPrivateKey(privateKeyPath));
    //            WechatPay2Credentials credentials = new WechatPay2Credentials(mchId, signer);
    //            CertificatesManager.getInstance().putMerchant(mchId,credentials,apiV3Key.getBytes(StandardCharsets.UTF_8));
    //
    //            verifier = new AutoUpdateCertificatesVerifier(credentials
    //                    , apiV3Key.getBytes("utf-8"));
    //            verifierMap.put(verifier.getValidCertificate().getSerialNumber() + "", verifier);
    //        } catch (UnsupportedEncodingException e) {
    //            e.printStackTrace();
    //        } catch (GeneralSecurityException e) {
    //            throw new RuntimeException(e);
    //        } catch (IOException e) {
    //            throw new RuntimeException(e);
    //        } catch (HttpCodeException e) {
    //            throw new RuntimeException(e);
    //        }
    //    } else {
    //        verifier = verifierMap.get(serialNumber);
    //    }
    //    return verifier;
    //}

    /**
     * 功能描述:获取平台证书，自动更新
     * 注意：这个方法内置了平台证书的获取和返回值解密
     *
     * @return com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier
     * @author zhouwenjie
     * @date 2021/6/13 20:29
     */
    public Verifier getVerifier(String serialNumber) {
        Verifier verifier;
        if (verifierMap.isEmpty() || !verifierMap.containsKey(serialNumber)) {
            verifierMap.clear();
            try {
                //刷新
                String mchId = wxpayConfigMapper.selectOneData("mchId");
                String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
                String mchSerialNo = wxpayConfigMapper.selectOneData("mchSerialNo");
                String apiV3Key = wxpayConfigMapper.selectOneData("apiV3Key");
                PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, getPrivateKey(privateKeyPath));
                WechatPay2Credentials credentials = new WechatPay2Credentials(mchId, signer);
                CertificatesManager.getInstance().putMerchant(mchId,credentials,apiV3Key.getBytes(StandardCharsets.UTF_8));
                verifier= CertificatesManager.getInstance().getVerifier(mchId);
                verifierMap.put(verifier.getValidCertificate().getSerialNumber() + "", verifier);
            } catch (NotFoundException e) {
                throw new RuntimeException(e);
            } catch (GeneralSecurityException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (HttpCodeException e) {
                throw new RuntimeException(e);
            }
        } else {
            verifier = verifierMap.get(serialNumber);
        }
        return verifier;
    }

    /**
     * 获取httpClient,用于之后请求微信接口
     * 用这个工具类的好处就是不用你自己去拼接签名参数了，每次请求会自动帮你创建签名
     * 详情可以点击进去看源码
     */
    public CloseableHttpClient getHttpClient(String serialNumber) {
        //获取平台证书
        Verifier verifier = getVerifier(serialNumber);
        if (verifier == null) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.SYSTEM_ERROR);
        }
        String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
        String mchSerialNo = wxpayConfigMapper.selectOneData("mchSerialNo");
        String mchId = wxpayConfigMapper.selectOneData("mchId");
        // 初始化httpClient  httpClient会在发送请求之前拦截并且自动根据请求信息创建签名
        CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, getPrivateKey(privateKeyPath))
                .withValidator(new WechatPay2Validator(verifier)).build();
        return httpClient;
    }


    public PrivateKey getPrivateKey(String url) {
        log.info("weChatPayPrivateKeyUrl:{}", url);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet(url);
            try (CloseableHttpResponse response = httpClient.execute(request)) {
                String content = EntityUtils.toString(response.getEntity(), "utf-8");
                String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                        .replace("-----END PRIVATE KEY-----", "")
                        .replaceAll("\\s+", "");
                KeyFactory kf = KeyFactory.getInstance("RSA");
                PrivateKey key = kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
                return key;
            }
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("获取私钥已完成");
        return null;
    }
    ///**
    // * 获取私钥。
    // *
    // * @return 私钥对象
    // */
    //public PrivateKey getPrivateKey(String fileName) {
    //    log.info("weChatPayPrivateKeyPath:{}", fileName);
    //
    //    try {
    //        ClassPathResource resource = new ClassPathResource(fileName);
    //        String content = IOUtils.toString(resource.getInputStream(), "utf-8");
    //        String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
    //                .replace("-----END PRIVATE KEY-----", "")
    //                .replaceAll("\\s+", "");
    //        KeyFactory kf = KeyFactory.getInstance("RSA");
    //        PrivateKey key = kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
    //        return key;
    //    } catch (NoSuchAlgorithmException e) {
    //        throw new RuntimeException("当前Java环境不支持RSA", e);
    //    } catch (InvalidKeySpecException e) {
    //        throw new RuntimeException("无效的密钥格式");
    //    } catch (UnsupportedEncodingException e) {
    //        e.printStackTrace();
    //    } catch (IOException e) {
    //        e.printStackTrace();
    //    }
    //    log.info("获取私钥已完成");
    //    return null;
    //}

    /**
     * 功能描述: 移动端支付请求签名
     *
     * @param prepayid
     * @param nonceStr
     * @return java.lang.String
     * @author zhouwenjie
     * @date 2021/6/10 9:09
     */
    @SneakyThrows
    public String appPaySign(String prepayid, String nonceStr, String timestamp) {

        String appAppid = applicationConfigMapper.selectOneData("app_appid");
        String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
        String signatureStr = Stream.of(appAppid, timestamp, nonceStr, prepayid)
                .collect(Collectors.joining("\n", "", "\n"));
        log.info("signatureStr={}", signatureStr);
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(privateKeyPath));
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }

    @SneakyThrows
    public String jsapiPaySign(String prepayid, String nonceStr, String timestamp) {
        String accountAppid = applicationConfigMapper.selectOneData("account_appid");
        String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
        String signatureStr = Stream.of(accountAppid, timestamp, nonceStr, prepayid)
                .collect(Collectors.joining("\n", "", "\n"));
        log.info("signatureStr={}", signatureStr);
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(privateKeyPath));
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }

    /**
     * 功能描述: 验证签名
     * 注意：使用微信支付平台公钥验签
     * Wechatpay-Signature 微信返签名
     * Wechatpay-Serial 微信平台证书序列号
     *
     * @return java.lang.String
     * @author zhouwenjie
     * @date 2021/6/10 9:09
     */
    @SneakyThrows
    public boolean verifySign(HttpServletRequest request, String body) {
        boolean verify = false;
        try {
            String wechatPaySignature = request.getHeader("Wechatpay-Signature");
            String wechatPayTimestamp = request.getHeader("Wechatpay-Timestamp");
            String wechatPayNonce = request.getHeader("Wechatpay-Nonce");
            String wechatPaySerial = request.getHeader("Wechatpay-Serial");
            //组装签名串
            String signStr = Stream.of(wechatPayTimestamp, wechatPayNonce, body)
                    .collect(Collectors.joining("\n", "", "\n"));
            //获取平台证书
            Verifier verifier = getVerifier(wechatPaySerial);
            //获取失败 验证失败
            if (verifier != null) {

            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(verifier.getValidCertificate());
            //放入签名串
            signature.update(signStr.getBytes(StandardCharsets.UTF_8));
            verify = signature.verify(Base64.getDecoder().decode(wechatPaySignature.getBytes()));
            }
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return verify;
    }

    /**
     * 功能描述: 获取X509Certificate
     *
     * @param
     * @return java.security.cert.X509Certificate
     * @author zhouwenjie
     * @date 2021/6/15 11:49
     */
    public X509Certificate getX509Certificate() {
        ClassPathResource resource = new ClassPathResource("wx/apiclient_cert.p12");
        KeyStore keyStore;
        X509Certificate certificate = null;
        try {
            String mchId = wxpayConfigMapper.selectOneData("mchId");
            keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(resource.getInputStream(), mchId.toCharArray());
            certificate = (X509Certificate) keyStore.getCertificate("Tenpay Certificate");
            certificate.checkValidity();
        } catch (KeyStoreException | IOException e) {
            e.printStackTrace();
        } catch (CertificateNotYetValidException e) {
            e.printStackTrace();
        } catch (CertificateExpiredException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return certificate;
    }

    /**
     * 证书和回调报文解密
     *
     * @param associatedData response.body.data[i].encrypt_certificate.associated_data 附加数据包（可能为空）
     * @param nonce          response.body.data[i].encrypt_certificate.nonce 加密使用的随机串初始化向量
     * @param ciphertext     response.body.data[i].encrypt_certificate.ciphertext Base64编码后的密文
     * @return the string
     * @throws GeneralSecurityException the general security exception
     */
    public String decryptToString(String associatedData, String nonce, String ciphertext) {
        String cert = null;
        try {
            String apiV3Key = wxpayConfigMapper.selectOneData("apiV3Key");
            //简化
            AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
            cert = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return cert;
    }

    /**
     * 敏感信息加解密-解密.
     * 使用商户私钥解密
     * <p>
     * `为了保证通信过程中敏感信息字段（如用户的住址、银行卡号、手机号码等）的机密性，
     * `微信支付API v3要求商户对上送的敏感信息字段进行加密。
     * `与之相对应，微信支付会对下行的敏感信息字段进行加密，商户需解密后方能得到原文
     *
     * @param ciphertext 密文
     */
    public String rsaDecryptOAEP(String ciphertext) {
        // 使用商户私钥解密
        String text = null;
        try {
            String privateKeyPath = wxpayConfigMapper.selectOneData("privateKeyPath");
            text = RsaCryptoUtil.decryptOAEP(ciphertext, getPrivateKey(privateKeyPath));
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return text;
    }

    /**
     * 敏感信息加解密-加密.
     * 使用微信支付平台证书中的公钥加密
     * 使用封装的RsaCryptoUtil
     * https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient
     *
     * @param message 需要加密的信息
     *                certificate 证书
     */
    public String rsaEncryptOAEP(String message) {
        String ciphertext = null;
        try {
            Verifier verifier = getVerifier(null);
            X509Certificate certificate = verifier.getValidCertificate();
            ciphertext = RsaCryptoUtil.encryptOAEP(message, certificate);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return ciphertext;
    }

    /**
     * 功能描述: 发起get请求
     * 动态返回自定义的实体类型
     *
     * @param method 请求方式 GET POST
     * @param url
     * @param t      泛型
     * @return T 实体类class
     * @author zhouwenjie
     * @date 2021/6/15 14:49
     */
    public <T> T wxHttpRequest(String method, String url, String requestData, Class<T> t) {
        T resultBean = null;
        T instance = null;
        CloseableHttpResponse response;
        CloseableHttpClient httpClient = null;
        try {
            if (!verifierMap.isEmpty()) {
                String bigInteger = verifierMap.keys().nextElement();
                //httpClient 内置有签名信息
                httpClient = getHttpClient(bigInteger);
            } else {
                httpClient = getHttpClient("1");
            }
            //创建实体，相当于new对象
            instance = t.newInstance();
            if ("get".equalsIgnoreCase(method)) {
                HttpGet httpGet = new HttpGet(url);
                httpGet.setHeader("Accept", "application/json");
                //完成签名并执行请求
                response = httpClient.execute(httpGet);
            } else {
                StringEntity entity = new StringEntity(requestData, StandardCharsets.UTF_8);
                entity.setContentType("application/json");
                HttpPost httpPost = new HttpPost(url);
                httpPost.setEntity(entity);
                httpPost.setHeader("Accept", "application/json");
                //完成签名并执行请求
                response = httpClient.execute(httpPost);
            }
            //如果状态码为200,就是正常返回
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                String result = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
                JSONObject jsonObject = JSON.parseObject(result);
                if (instance instanceof JSONObject) {
                    resultBean = (T) jsonObject;
                } else {
                    resultBean = JSONObject.parseObject(jsonObject.toString(), t);
                }
            } else if (statusCode == 204) {
                log.info("关单成功");
                String code = "204";
                resultBean = (T) code;
            } else {
                log.error("错误状态码 = " + statusCode + ",返回的错误信息 = " + EntityUtils.toString(response.getEntity()));
                throw new ErrorCodeException(ErrorCodeExceptionEnum.SYSTEM_ERROR);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultBean;
    }

    ///**
    // * 功能描述:生成单个订单预付单
    // *
    // * @param totalPrice
    // * @param out_trade_no
    // * @param description
    // * @return java.util.Map<java.lang.String, java.lang.String>
    // * @author zhouwenjie
    // * @date 2021/6/10 10:16
    // */
    //public static Map<String, String> createOrder(Integer totalPrice, String out_trade_no, String
    //        description,String notifyUrl) {
    //    WeChatPayRequestVo wxOnePayVo = new WeChatPayRequestVo(totalPrice);
    //    wxOnePayVo.setAppid(wxPayEntity.appid);
    //    wxOnePayVo.setMchid(wxPayEntity.mchId);
    //    wxOnePayVo.setNotify_url(notifyUrl);
    //    wxOnePayVo.setDescription(description);
    //    wxOnePayVo.setOut_trade_no(out_trade_no);
    //    //计算失效时间  15分钟订单关闭
    //    //wxOnePayVo.setTime_expire("20220928181010");
    //    String reqdata = JSON.toJSONString(wxOnePayVo);
    //    log.info("异步回掉地址：{}", wxOnePayVo.getNotify_url());
    //
    //    Map<String, String> returnMap = getPayReturnMap("POST", reqdata, wxPayEntity.singleAPPUrl);
    //    return returnMap;
    //}

    /**
     * 生成单个预付单
     *
     * @param weChatCreateOrderRequestVo
     * @return
     */
    public Map<String, String> createOrder(WeChatCreateOrderVO weChatCreateOrderRequestVo) {
        WeChatPayVO weChatPayRequestVo = new WeChatPayVO(weChatCreateOrderRequestVo.getTotalPrice().intValue(), weChatCreateOrderRequestVo.getOpenId());
        String mchId = wxpayConfigMapper.selectOneData("mchId");
        String appAppid = applicationConfigMapper.selectOneData("app_appid");
        weChatPayRequestVo.setAppid(appAppid);
        weChatPayRequestVo.setMchid(mchId);
        weChatPayRequestVo.setNotify_url(weChatCreateOrderRequestVo.getNotifyUrl());
        weChatPayRequestVo.setDescription(weChatCreateOrderRequestVo.getDescription());
        weChatPayRequestVo.setOut_trade_no(weChatCreateOrderRequestVo.getOutTradeNo());
        String requestData = JSON.toJSONString(weChatPayRequestVo);
        log.info("微信请求入参：{}", weChatPayRequestVo.toString());
        return getPayReturnMap(WeChatRequestConstants.WeChatPOSTMethod, requestData, WxPayTypeEnum.getNameByKey(weChatCreateOrderRequestVo.getPayType()));
        //return returnMap;
    }

    public Map<String, String> getPayReturnMap(String method, String requestData, String url) {
        HashMap<String, String> returnMap = new HashMap<String, String>();
        JSONObject jsonObject = wxHttpRequest(method, url, requestData, JSONObject.class);
        log.info("入参参数：{}", jsonObject);
        //处理成功
        String prepay_id = (String) jsonObject.get("prepay_id");
        //生成代签名的支付信息
        String nonceStr = UUID.randomUUID().toString(true);
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String signature = appPaySign(prepay_id, nonceStr, timestamp);
        String mchId = wxpayConfigMapper.selectOneData("mchId");
        String appAppid = applicationConfigMapper.selectOneData("app_appid");
        returnMap.put("appid", appAppid);
        returnMap.put("partnerid", mchId);
        returnMap.put("prepayid", prepay_id);
        returnMap.put("package", "Sign=WXPay");
        returnMap.put("nonceStr", nonceStr);
        returnMap.put("timestamp", timestamp);
        returnMap.put("paySign", signature);
        log.info("returnMap:{}", returnMap);
        return returnMap;
    }

    /**
     * 功能描述:退款操作
     * 注意：
     * 1、交易时间超过一年的订单无法提交退款
     * <p>
     * 2、微信支付退款支持单笔交易分多次退款（不超50次），多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交，请不要更换退款单号，请使用原商户退款单号
     * <p>
     * 3、错误或无效请求频率限制：6qps，即每秒钟异常或错误的退款申请请求不超过6次
     * <p>
     * 4、每个支付订单的部分退款次数不能超过50次
     * <p>
     * 5、如果同一个用户有多笔退款，建议分不同批次进行退款，避免并发退款导致退款失败
     * <p>
     * 6、申请退款接口的返回仅代表业务的受理情况，具体退款是否成功，需要通过退款查询接口获取结果
     * <p>
     * 7、一个月之前的订单申请退款频率限制为：5000/min
     */
    public WeChatRefundReturnVO refundOrder(WeChatRefundVO wxRefundInfoVo) {
        //wxRefundInfoVo.setNotify_url(wxPayEntity.refund_notify_url);
        String requestData = JSON.toJSONString(wxRefundInfoVo);
        WeChatRefundReturnVO wxRefundReturnInfoVo = wxHttpRequest("POST", wxPayEntity.refundUrl, requestData, WeChatRefundReturnVO.class);
        return wxRefundReturnInfoVo;
    }

    /**
     * 功能描述: 查询订单
     * 查询订单可通过微信支付订单号和商户订单号两种方式查询，两种查询方式返回结果相同
     *
     * @param transaction_id
     * @return void
     * @author zhouwenjie
     * @date 2021/6/15 16:08
     */
    public QueryOrderVO QueryOrder(String transaction_id) {
        String mchId = wxpayConfigMapper.selectOneData("mchId");
        String url = String.format(wxPayEntity.queryUrl, transaction_id, mchId);
        QueryOrderVO notifyResourceVO = wxHttpRequest("GET", url, null, QueryOrderVO.class);
        return notifyResourceVO;
    }

    /**
     * 功能描述: 关闭订单
     * POST  接口响应204，无内容  即成功
     *
     * @param out_trade_no 商户系统内部订单号
     * @return void
     * @author zhouwenjie
     * @date 2021/6/15 16:08
     */
    public String CloseOrder(String out_trade_no) {
        String url = String.format(wxPayEntity.closeUrl, out_trade_no);
        //请求body参数
        String mchId = applicationConfigMapper.selectOneData("mchId");
        String requestData = "{\"mchid\": \"" + mchId + "\"}";
        String code = wxHttpRequest("POST", url, requestData, String.class);
        return code;
    }
}

